package cn.mazexal.examapi.services;

import cn.hutool.core.util.IdUtil;
import cn.mazexal.examapi.dataware.UserMapper;
import cn.mazexal.examapi.dataware.UserVipLogMapper;
import cn.mazexal.examapi.dataware.UserVipStatusMapper;
import cn.mazexal.examapi.models.db.*;
import cn.mazexal.examapi.models.enums.FromTypeEnum;
import cn.mazexal.examapi.models.vo.UserVipResItemVO;
import cn.mazexal.examapi.models.vo.UserVipResVO;
import cn.mazexal.examapi.utils.DateUtils;
import com.alibaba.fastjson.JSON;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author: Mazexal
 * Date: 2025/8/23
 */
@Service
@Slf4j
public class UserVipService {

    @Resource
    private UserVipLogMapper userVipLogMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private UserVipStatusMapper userVipStatusMapper;
    @Autowired
    private CommonService commonService;

    @Transactional(rollbackFor = Exception.class)
    public boolean addUserVIP(Long userId,
                              FromTypeEnum fromTypeEnum,
                              Long orderId,
                              BigDecimal costMoney,
                              VipPriceDO vipPriceDO) {
        Long now = System.currentTimeMillis();

        Long calcDuration = DateUtils.calcDuration(vipPriceDO.getDuration(), vipPriceDO.getUnit());
        long beginTime = now;

        // 过期时间要累加
        // 根据 VipPriceDO 里的examBankId来获取VIP账户
        String examBankIdStr = vipPriceDO.getExamBankId();

        List<Long> examBankIds = getExamBankIds(examBankIdStr);
        // 首先获取用户所有的账户
        List<UserVipStatusDO> userVipStatusDOList = userVipStatusMapper.getUserVipStatus(userId);
        // 以examBankId为key便于查找
        Map<Long, UserVipStatusDO> userVipStatusDOMap =
                userVipStatusDOList.stream().collect(Collectors.toMap(UserVipStatusDO::getExamBankId, v -> v));
        UserVipStatusDO common = userVipStatusDOMap.get(0L);
        if (common.getVipExpireTime() > beginTime) {
            beginTime = common.getVipExpireTime();
        }
        for (Long examBankId : examBankIds) {
            if (examBankId == 0L) {
                // 如果是0L的话, 特殊处理, 直接更新就行.
                common.setVipExpireTime(calcDuration + beginTime);
                common.setUpdateTime(now);
                // 直接更新
                userVipStatusMapper.updateById(common);
                // 如果用户已经买了其他账户的VIP, 还需要更新其他账户的, 到时候哪个大用哪个.
                for (UserVipStatusDO userVipStatusDO : userVipStatusDOList) {
                    if (userVipStatusDO.getVipExpireTime() > beginTime && userVipStatusDO.getExamBankId() != 0) {
                        userVipStatusDO.setVipExpireTime(calcDuration + userVipStatusDO.getVipExpireTime());
                        userVipStatusDO.setUpdateTime(now);
                        userVipStatusMapper.updateById(userVipStatusDO);
                    }
                }

            } else {
                UserVipStatusDO examBankVip = userVipStatusDOMap.get(examBankId);
                if (examBankVip == null) {
                    // 如果为空, 说明之前没办理, 则添加
                    UserVipStatusDO userVipStatusDO = loadUserVipStatus(userId, now, examBankId,
                            beginTime + calcDuration, 1);
                    userVipStatusMapper.insert(userVipStatusDO);
                } else {
                    // 办理过了, 检查VIP过期时间和beginTime, 哪个大, 哪个就是新的开始时间.
                    long examBankVipExpireTime = examBankVip.getVipExpireTime();
                    if (examBankVip.getVipExpireTime() < beginTime) {
                        examBankVipExpireTime = beginTime;
                    }
                    examBankVip.setVipExpireTime(examBankVipExpireTime + calcDuration);
                    examBankVip.setUpdateTime(now);
                    userVipStatusMapper.updateById(examBankVip);
                }
            }
        }
        UserVipLogDO userVipLogDO = new UserVipLogDO();
        userVipLogDO.setId(IdUtil.getSnowflakeNextId());
        userVipLogDO.setUserId(userId);
        userVipLogDO.setDuration(vipPriceDO.getDuration());
        userVipLogDO.setUnit(vipPriceDO.getUnit());
        userVipLogDO.setVipExpireTime(common.getVipExpireTime());
        userVipLogDO.setFromType(fromTypeEnum.getValue());
        userVipLogDO.setCreateTime(System.currentTimeMillis());
        userVipLogDO.setOrderId(orderId);
        userVipLogDO.setCostMoney(costMoney);
        userVipLogDO.setConfigId(vipPriceDO.getId());
        userVipLogDO.setExamBankId(vipPriceDO.getExamBankId());
        return userVipLogMapper.insert(userVipLogDO) > 0;
    }

    private UserVipStatusDO loadUserVipStatus(Long userId, Long now, Long examBankId, Long vipExpireTime,
                                              Integer vipStatus) {
        UserVipStatusDO userVipStatusDO = new UserVipStatusDO();
        userVipStatusDO.setId(IdUtil.getSnowflakeNextId());
        userVipStatusDO.setUserId(userId);
        userVipStatusDO.setVipStatus(vipStatus);
        userVipStatusDO.setExamBankId(examBankId);
        userVipStatusDO.setVipExpireTime(vipExpireTime);
        userVipStatusDO.setCreateTime(now);
        userVipStatusDO.setUpdateTime(now);
        return userVipStatusDO;
    }

    private List<Long> getExamBankIds(String examBankIdStr) {
        if (StringUtils.isBlank(examBankIdStr)) {
            examBankIdStr = "0";
        }
        String[] examBankIdArr = examBankIdStr.split(",");
        return Arrays.asList(examBankIdArr).stream().map(Long::valueOf).collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean handleRefund(FinOrderDO finOrderDO, FromTypeEnum fromTypeEnum) {
        Long userId = finOrderDO.getUserId();
        VipPriceDO vipPriceDO = JSON.parseObject(finOrderDO.getExtraInfo(), VipPriceDO.class);
        Integer duration = vipPriceDO.getDuration();
        Integer unit = vipPriceDO.getUnit();
        Long calcVipExpireTime = DateUtils.calcDuration(duration, unit);

        String examBankIdStr = vipPriceDO.getExamBankId();
        List<Long> examBankIds = getExamBankIds(examBankIdStr);
        Long now = System.currentTimeMillis();
        // 以examBankId为key便于查找
        List<UserVipStatusDO> userVipStatusDOList = userVipStatusMapper.getUserVipStatus(userId);
        Map<Long, UserVipStatusDO> userVipStatusDOMap =
                userVipStatusDOList.stream().collect(Collectors.toMap(UserVipStatusDO::getExamBankId, v -> v));
        // 扣除的话就简单了, 直接减去就行
        long vipExpireTime = now;
        for (Long examBankId : examBankIds) {
            if (examBankId == 0L) {
                for (UserVipStatusDO userVipStatusDO : userVipStatusDOList) {
                    userVipStatusDO.setVipExpireTime(userVipStatusDO.getVipExpireTime() - calcVipExpireTime);
                    userVipStatusDO.setUpdateTime(now);
                    userVipStatusMapper.updateById(userVipStatusDO);
                }
            } else {
                UserVipStatusDO examBankVip = userVipStatusDOMap.get(examBankId);
                examBankVip.setVipExpireTime(examBankVip.getVipExpireTime() - calcVipExpireTime);
                examBankVip.setUpdateTime(now);
                userVipStatusMapper.updateById(examBankVip);
            }
            vipExpireTime = now - calcVipExpireTime;
        }
        BigDecimal costMoney = finOrderDO.getOrderAmount();
        Long orderId = finOrderDO.getId();
        UserVipLogDO userVipLogDO = new UserVipLogDO();
        userVipLogDO.setId(IdUtil.getSnowflakeNextId());
        userVipLogDO.setUserId(userId);
        userVipLogDO.setDuration(duration);
        userVipLogDO.setUnit(unit);
        userVipLogDO.setVipExpireTime(vipExpireTime);
        userVipLogDO.setFromType(fromTypeEnum.getValue());
        userVipLogDO.setCreateTime(System.currentTimeMillis());
        userVipLogDO.setOrderId(orderId);
        userVipLogDO.setCostMoney(costMoney.multiply(BigDecimal.valueOf(-1)));
        userVipLogDO.setConfigId(vipPriceDO.getId());
        userVipLogDO.setExamBankId(vipPriceDO.getExamBankId());
        return userVipLogMapper.insert(userVipLogDO) > 0;
    }

    @Transactional(rollbackFor = Exception.class)
    public void initUserVip(Long userId, long now) {
        // 因为是初始化, 所以不添加用户VIPLog.
        UserVipStatusDO userVipStatusDO = loadUserVipStatus(userId, now, 0L, now, 1);
        userVipStatusMapper.insert(userVipStatusDO);

    }

    public Long getUserVipExpireTime(Long userId, Long examBankId) {
        List<UserVipStatusDO> userVipStatusDOList = userVipStatusMapper.getUserVipStatus(userId);
        Map<Long, UserVipStatusDO> userVipStatusDOMap =
                userVipStatusDOList.stream().collect(Collectors.toMap(UserVipStatusDO::getExamBankId, v -> v));
        UserVipStatusDO userVipStatusDO = userVipStatusDOMap.get(examBankId);
        UserVipStatusDO common = userVipStatusDOMap.get(0L);
        if (userVipStatusDO == null || userVipStatusDO.getVipExpireTime() < common.getVipExpireTime()) {
            return common.getVipExpireTime();
        } else {
            return userVipStatusDO.getVipExpireTime();
        }
    }

    public List<UserVipResItemVO> getUserVipExpireTimeList(Long userId) {
        List<UserVipStatusDO> userVipStatusDOList = userVipStatusMapper.getUserVipStatus(userId);
        Map<Long, UserVipStatusDO> userVipStatusDOMap =
                userVipStatusDOList.stream().collect(Collectors.toMap(UserVipStatusDO::getExamBankId, v -> v));
        List<Long> examBandList = commonService.getExamBankList();
        UserVipStatusDO common = userVipStatusDOMap.get(0L);
        List<UserVipResItemVO> userVipResItemVOList = new ArrayList<>();
        for (Long examBankId : examBandList) {
            UserVipStatusDO userVipStatusDO = userVipStatusDOMap.get(examBankId);
            Long expireTime;
            if (userVipStatusDO == null || userVipStatusDO.getVipExpireTime() < common.getVipExpireTime()) {
                expireTime = common.getVipExpireTime();
            } else {
                expireTime = userVipStatusDO.getVipExpireTime();
            }
            UserVipResItemVO userVipResItemVO = new UserVipResItemVO();
            userVipResItemVO.setExamBankId(examBankId);
            userVipResItemVO.setExpireTime(expireTime);
            userVipResItemVOList.add(userVipResItemVO);
        }
        return userVipResItemVOList;
    }

    // 这里是用来给苹果订阅来检测是否是VIP的, 以此判断是否需要立马处理, 如果是VIP的话, 就可以通过回调处理, 如果不是VIP, 需要立马处理, 不用考虑升降级.
    public boolean isVip(Long userId, String examBankIds) {
        Long now = System.currentTimeMillis();
        List<Long> examBankIdList = new ArrayList<>();
        Arrays.stream(examBankIds.split(",")).map(Long::valueOf).forEach(examBankIdList::add);
        List<UserVipStatusDO> userVipStatus = userVipStatusMapper.getUserVipStatus(userId);
        Map<Long, UserVipStatusDO> userVipStatusDOMap =
                userVipStatus.stream().collect(Collectors.toMap(UserVipStatusDO::getExamBankId, v -> v));

        for (Long examBankId : examBankIdList) {
            UserVipStatusDO userVipStatusDO = userVipStatusDOMap.get(examBankId);
            if (userVipStatusDO == null || userVipStatusDO.getVipExpireTime() < now) {
                return false;
            }
        }
        return true;
    }
}
